/*
 * state_machine.c
 *
 *  Created on: Jul 20, 2012
 *      Author: Eugene Parkhomenko
 */

#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#include "sm_error.h"
#include "sm_debug.h"
#include "sm_types.h"
#include "state_machine_priv.h"
#include "state_machine_pub.h"

static void* sm_state_trasaction_handler_thread(void *pvData)
{
    ST_STH_PRIV_DESCRIPTOR_TAG *pstSthDescriptor = (ST_STH_PRIV_DESCRIPTOR_TAG *)pvData;

    while(FALSE == pstSthDescriptor->fStopSthThread)
    {
        // read from event queue
        // go through transition table
        // if there is transaction for current state and event
        // check transaction guard
        // then perform transaction
        // call: current state EXIT function
        // set: current state = new state
        // call: transaction ACTION function
        // call: new state ENTRY function
        sleep(1);
    }

    return NULL;
}

EN_SM_ERR_CODE SM_init(SM_INSTANCE_DESCRIPTOR *ptSthInstance, ST_SM_STATE *pstStateTable, ST_SM_TRANSITION *pstTransitionTable)
{
    EN_SM_ERR_CODE enRetCode = enSM_ERR_CODE_OK;
    ST_STH_PRIV_DESCRIPTOR_TAG *pstSthDescriptor = NULL;

    if ((NULL == pstStateTable) ||
        (NULL == pstTransitionTable) ||
        (NULL == ptSthInstance))
    {
        enRetCode = enSM_ERR_CODE_NULL_POINTER;
        SM_ERRNL(TRUE, "NULL pointer on input");
        goto exit;
    }

    pstSthDescriptor = malloc(sizeof(ST_STH_PRIV_DESCRIPTOR_TAG));
    if (NULL == pstSthDescriptor)
    {
        enRetCode = enSM_ERR_CODE_MEMORY_ALLOC_FAILED;
        SM_ERRNL(TRUE, "Memory allocation failed");
        goto exit;
    }

    pstSthDescriptor->pstStateTable = pstStateTable;
    pstSthDescriptor->pstTransactionTable = pstTransitionTable;
    // Assume that first state in the table is initial one
    pstSthDescriptor->ulCurrentState = pstStateTable[0].ulId;
    pstSthDescriptor->fStopSthThread = FALSE;

    if (0 != pthread_create(&pstSthDescriptor->stSthHandler, NULL, sm_state_trasaction_handler_thread, (void *)pstSthDescriptor))
    {
        enRetCode = enSM_ERR_CODE_THREAD_CREAT_FAILED;
        SM_ERRNL(TRUE, "pthread_create failed last error: %d", errno);
        goto mem_free;
    }

    *ptSthInstance = (SM_INSTANCE_DESCRIPTOR) pstSthDescriptor;
    goto exit;

mem_free:
    free(pstSthDescriptor);
exit:
    return enRetCode;
}

EN_SM_ERR_CODE SM_stop(SM_INSTANCE_DESCRIPTOR tSthInstance)
{
    ST_STH_PRIV_DESCRIPTOR_TAG *pstSthDescriptor = (ST_STH_PRIV_DESCRIPTOR_TAG *)tSthInstance;
    pstSthDescriptor->fStopSthThread = TRUE;
    pthread_join(pstSthDescriptor->stSthHandler, NULL);

    free(pstSthDescriptor);
    pstSthDescriptor = NULL;

    return enSM_ERR_CODE_OK;
}

EN_SM_ERR_CODE SM_handle_event(unsigned int ulEvent)
{
	EN_SM_ERR_CODE enRetCode = enSM_ERR_CODE_OK;

	// add event to the queue

exit:
	return enRetCode;
}
